﻿Imports System.Reflection
Imports System.Reflection.Emit
Imports VBParser
Imports System.Collections.ObjectModel

Namespace Compiler.Semantic

    Public Class NamedTypeResolver
        Inherits TypeResolver

        Private ReadOnly m_isConstructedType As Boolean
        Private ReadOnly m_name As Name

        Public Sub New(ByVal typeName As NamedTypeName, ByVal context As ResolveContext)
            MyBase.New(typeName, context)

            m_name = typeName.Name
            m_isConstructedType = typeName.Type = TreeType.ConstructedType

            StartResolve()
        End Sub

        Private Sub StartResolve()
            'resolve a named type
            'named type could be a constructed type or a simple named type
            'the name could be a simple name, qualified name or special name
            'this class will handle them all.

            Dim typeArgCount As Integer
            If m_isConstructedType Then
                typeArgCount = TryCast(TypeName, ConstructedTypeName).TypeArguments.Count
            Else
                typeArgCount = 0
            End If

            'search in the following steps:
            '1. Current hierarchy
            '2. Imported container's hierarchy
            '3. Full name as an external type

            Dim resultFlag As Boolean = False

            If m_name.Type = TreeType.SimpleName Then
                'simple name could be a type or type parameter
                Dim simpleName As SimpleName = TryCast(m_name, SimpleName)

                Dim binder As Binder = New TypeDeclBinder(Symbol.Get(simpleName.Name), typeArgCount)

                Dim result As NameContainer = SearchInHierarchy(binder, Context.CurrentContainer)

                resultFlag = TryAddResult(result)

            ElseIf m_name.Type = TreeType.QualifiedName Then
                'resolve method:
                '1. Resolve left most name. 
                'Dim qName As QualifiedName = TryCast(m_name, QualifiedName)
                'Dim rightPart As Name = Nothing
                'Dim leftMost As Name = GetLeftMostName(qName, rightPart)

                '2. If Global, try search from global scope (ignore imports and current environment)
                'If leftMost.Type = TreeType.GlobalNamespaceName Then
                ''create right part typename
                'Dim rightPartTypeName As TypeName
                'If m_isConstructedType Then
                '    Dim constructName = TryCast(TypeName, ConstructedTypeName)
                '    rightPartTypeName = New ConstructedTypeName(rightPart, constructName.TypeArguments, New Span(rightPart.Span.Start, constructName.Span.Finish))
                'Else
                '    rightPartTypeName = New NamedTypeName(rightPart, New Span(rightPart.Span.Start, TypeName.Span.Finish))
                'End If

                ''create global context
                'Dim globalContext As New ResolveContext(Context.CompileUnit, Context.CompileUnit, Context.Level)
                'Dim globalResolveHelper = TypeResolver.Create(rightPart, globalContext)
                'End If
                '3. If MyBase, treat environment as a class description, search it's parent
                '4. If a name, resolve it as a name container
                '5. In the environment of this container, search next.

                'TODO Continue the implementation
            End If

            If Not resultFlag Then
                'Try imports
                'TODO implement imports resolve
            End If

EXTERNAL_RESOLVE:
            If Not resultFlag Then
                'Try extern resolve
                'TODO implement extern resolve
            End If
        End Sub

        'Private Function GetLeftMostName(ByVal qName As QualifiedName, ByRef rightPart As Name) As Name
        '    Dim current = qName
        '    Dim qualifier As Name

        '    Do
        '        qualifier = current.Qualifier
        '        If qualifier.Type = TreeType.QualifiedName Then
        '            current = TryCast(qualifier, QualifiedName)
        '        Else
        '            'qualifer is a simple name or special name
        '            ' the current is the righPart
        '            rightPart = current
        '            Return qualifier
        '        End If
        '    Loop While qualifier IsNot Nothing

        '    'qualifier is nothing, it must be something wrong
        '    Throw New ArgumentException("The left most qualifier is a null reference. It is unexpected.", "qName")
        'End Function

        Private Shared Function AllTypeArgsAreEmpty(ByVal typeArgs As TypeArgumentCollection) As Boolean
            Dim result As Boolean = True
            For Each arg In typeArgs
                result = (arg Is Nothing) AndAlso result
            Next

            Return result
        End Function

        Private Function TryAddResult(ByVal result As NameContainer) As Boolean
            'if the namecontainer is a type, then
            '1. if a constructed type, continue to resolve each type arguments
            Dim typeDesc As TypeDescription = TryCast(result, TypeDescription)
            If typeDesc Is Nothing Then Return False

            If Not m_isConstructedType Then
                'simple type, create a standard type reference
                AddResult(New StandardTypeReference(typeDesc))
                Return True
            Else
                'constructed type

                Dim genericType As New StandardTypeReference(typeDesc)

                'for most constructed type, a ConstructedTypeReference is needed. 
                Dim constructedName = TryCast(TypeName, ConstructedTypeName)

                Dim typeArguments(constructedName.TypeArguments.Count - 1) As TypeReference
                Dim haveProblems As Boolean = False

                'if in GetType expression, and the type parameters are empty, a standard type reference is enough
                If Context.Level = ResolveLevel.GetTypeExp Then
                    If AllTypeArgsAreEmpty(constructedName.TypeArguments) Then
                        AddResult(genericType)
                        Return True
                    End If
                End If

                'resolve type arguments
                For i = 0 To constructedName.TypeArguments.Count - 1
                    Dim typeArg = constructedName.TypeArguments(i)
                    Dim typeArgResolver = TypeResolver.Create(typeArg, Context)

                    'If resolve failed because...
                    If typeArgResolver.ResultSet.Count > 1 Then
                        Context.CompileUnit.SemanticErrorList.Add( _
                            New SemanticError(SemanticErrorType.TypeResolveAmbiguity, typeArg.Span))

                        haveProblems = True
                    ElseIf typeArgResolver.ResultSet.Count = 0 Then
                        Context.CompileUnit.SemanticErrorList.Add( _
                            New SemanticError(SemanticErrorType.TypeResolveFailure, typeArg.Span))
                        haveProblems = True
                    Else
                        'succeeded!
                        typeArguments(i) = typeArgResolver.ResultSet(0)
                    End If
                Next

                'failed in somewhere
                If haveProblems Then Return False

                'create constructed type reference
                AddResult(New ConstructedTypeReference(genericType, typeArguments))
                Return True
            End If
        End Function

        Private Shared Function SearchInHierarchy(ByVal binder As Binder, ByVal currentContainer As NameContainer) As NameContainer
            '1. search current container's name
            '2. search current container's children
            '3. search parent of step 1, 2

            Do Until currentContainer Is Nothing
                If binder = currentContainer.IdentifyBinder Then
                    Return currentContainer
                Else
                    Dim result = currentContainer.SearchChild(binder)
                    If result IsNot Nothing Then
                        Return result
                    End If
                End If

                currentContainer = currentContainer.Parent
            Loop

            'really can't find
            Return Nothing
        End Function
    End Class
End Namespace

